﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;

namespace Aes256GcmExample
{
    public class UtilAes_GCM
    {
        /// <summary>
        /// 使用BouncyCastle AEAD_AES_256_GCM 解密
        /// </summary>
        /// <param name="key">key:32位字符</param>
        /// <param name="cipherData">密文(Base64字符)</param>
        /// <param name="associatedData">附加数据可能null</param>
        /// <returns></returns>
        public static string AesGcmDecryptByBouncyCastle(string key, string cipherData, string associatedData)
        {
            var cipherData_byte = Convert.FromBase64String(cipherData);//前12位是随机字符串nonce
            var nonce_byte = new byte[12];
            Array.Copy(cipherData_byte, 0, nonce_byte, 0, 12);

            string nonce = Encoding.UTF8.GetString(nonce_byte);
            var associatedBytes = associatedData == null ? null : Encoding.UTF8.GetBytes(associatedData);

            var gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            var parameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(key)),
                128,  //128 = 16 * 8 => (tag size * 8)
                Encoding.UTF8.GetBytes(nonce),
                associatedBytes);
            gcmBlockCipher.Init(false, parameters);

            var data = new byte[cipherData_byte.Length - 12];
            Array.Copy(cipherData_byte, 12, data, 0, cipherData_byte.Length - 12);
            var plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];

            var length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
            gcmBlockCipher.DoFinal(plaintext, length);
            return Encoding.UTF8.GetString(plaintext);
        }

        /// <summary>
        /// 使用使用BouncyCastle AEAD_AES_256_GCM 加密
        /// </summary>
        /// <param name="key">key32位字符</param>
        /// <param name="plainData">明文</param>
        /// <param name="associatedData">附加数据可能null</param>
        /// <returns></returns>
        public static string AesGcmEncryptByBouncyCastle(string key, string plainData, string associatedData)
        {
            string nonce = Guid.NewGuid().ToString("N").Substring(0, 12);//随机串12位
            var associatedBytes = associatedData == null ? null : Encoding.UTF8.GetBytes(associatedData);

            var gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            var parameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(key)),
                128, //128 = 16 * 8 => (tag size * 8)
                Encoding.UTF8.GetBytes(nonce),
                associatedBytes);
            gcmBlockCipher.Init(true, parameters);

            var data = Encoding.UTF8.GetBytes(plainData);
            var cipherData = new byte[gcmBlockCipher.GetOutputSize(data.Length)];

            var length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, cipherData, 0);
            gcmBlockCipher.DoFinal(cipherData, length);
            byte[] nonce_byte = Encoding.UTF8.GetBytes(nonce);
            byte[] byteFinal = new byte[cipherData.Length + 12];
            Array.Copy(nonce_byte, 0, byteFinal, 0, nonce_byte.Length);
            Array.Copy(cipherData, 0, byteFinal, nonce_byte.Length, cipherData.Length);
            return Convert.ToBase64String(byteFinal);
        }
    }

    public static class Aes_GCM_Helper
    {
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string Encrypt(string data,string key)
        {
            try
            {
                String ciphertext = UtilAes_GCM.AesGcmEncryptByBouncyCastle(key, data, null);
                return ciphertext;
            }
            catch (Exception e)
            {
                throw;
            }
        }
        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string Decrypt(string data, string key)
        {
            try
            {
                String decrypted = UtilAes_GCM.AesGcmDecryptByBouncyCastle(key, data, null);
                return decrypted;
            }
            catch (Exception e)
            {
                throw;
            }
        }
        /// <summary>
        /// 解密数据库字符串，数据库只有密码加密，需要截取处理解密处理
        /// </summary>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static string DecryptConnectionString(string connectionString)
        {
            //Host=127.0.0.1:5432;database=postgres;user id=postgres;password=postgres;searchpath=mycim;
            string str = "";
            string strFirst = connectionString.Substring(0, connectionString.IndexOf("password=") + 9);
            string pwd = connectionString.Substring(connectionString.IndexOf("password=") + 9);
            string strThird = pwd.Substring(pwd.IndexOf(";"));
            pwd = pwd.Substring(0, pwd.IndexOf(";"));
            //pwd = Aes_GCM_Helper.Decrypt(pwd);
            str = $@"{strFirst}{pwd}{strThird}";
            return str;
        }
    }
}
